package com.change.service.account;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.change.core.constant.Constants;
import com.change.core.dto.Account;
import com.change.core.page.Page;
import com.change.core.web.request.AccountContext;
import com.change.dto.params.ChangePwdParams;
import com.change.dto.params.RegisterParams;
import com.change.dto.req.QueryAccountInfoReq;
import com.change.dto.req.RegisterReq;
import com.change.dto.req.SaveAccountInfoReq;
import com.change.dto.resp.LoginResultModel;
import com.change.dto.resp.QueryAccountInfoResp;
import com.change.dto.resp.UserInfoResultModel;
import com.change.dto.resp.UserLoginResp;
import com.change.entity.account.AccountInfo;
import com.change.entity.company.CompanyInfo;
import com.change.entity.relation.AccountCompanyRelation;
import com.change.exception.BizException;
import com.change.exception.ResultStatus;
import com.change.mapper.account.AccountInfoMapper;
import com.change.mapper.company.CompanyInfoMapper;
import com.change.mapper.relation.AccountCompanyRelationMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;

/**
* @author 坏孩子是你啊
* @date  2022-07-15
*/
@Slf4j
@Service
public class AccountInfoService extends ServiceImpl<AccountInfoMapper, AccountInfo>{
    private final AccountInfoMapper accountInfoMapper;
    private final CompanyInfoMapper companyInfoMapper;
    private final AccountCompanyRelationMapper relationMapper;


    public AccountInfoService(AccountInfoMapper accountInfoMapper,
                              CompanyInfoMapper companyInfoMapper,
                              AccountCompanyRelationMapper relationMapper) {
        this.accountInfoMapper = accountInfoMapper;
        this.companyInfoMapper = companyInfoMapper;
        this.relationMapper = relationMapper;
    }

    public Page<List<QueryAccountInfoResp>> getPage(Page page, QueryAccountInfoReq req) {
        page.offset();
        List<QueryAccountInfoResp> items = this.accountInfoMapper.findAccountInfoPage(page, req);
        long total = this.accountInfoMapper.findAccountInfoPageTotal(req);
        page.setItems(items);
        page.setTotal(total);
        return page;
    }

    public QueryAccountInfoResp detail(Integer id) {
        AccountInfo accountInfo =  this.accountInfoMapper.selectById(id);
        QueryAccountInfoResp resp = new QueryAccountInfoResp();
        BeanUtil.copyProperties(accountInfo, resp);
        return resp;
    }

    @Transactional(rollbackFor = Exception.class)
    public AccountInfo save(SaveAccountInfoReq req) {

        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public AccountInfo update(SaveAccountInfoReq req) {
         return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public Integer delete(Integer id) {
        return null;
    }

    /**
     * 注册会员
     * @param req
     * @return
     */
    public Integer register(RegisterReq req) {
        QueryWrapper<AccountInfo> userQuery = Wrappers.query();
        userQuery.lambda().eq(AccountInfo::getAccount,req.getUsername())
                .eq(AccountInfo::getPassword,req.getPassword());
        AccountInfo accountInfo = accountInfoMapper.selectOne(userQuery);
        if(accountInfo == null){
            accountInfo = new AccountInfo();
            accountInfo.setAccount(req.getUsername());
            accountInfo.setPassword(req.getPassword());
            accountInfoMapper.insert(accountInfo);
        }
        return accountInfo.getId();
    }

    /**
     * 账号密码登录
     * @param req
     * @return
     */
    public LoginResultModel login(RegisterReq req) {
        // 返回封装登陆对象
        LoginResultModel resultModel = new LoginResultModel();

        UserLoginResp userLoginResp = accountInfoMapper.selectOneByAccountInfo(req);
        if(null == userLoginResp){
            throw new BizException(ResultStatus.Common.NOT_FOUNT_USER);
        }
        UserLoginResp loginResp = getUserLoginResp(userLoginResp);

        Map<String, Object> claims = new HashMap<>();
        claims.put("uuid", loginResp.getBrowserUuid());
        LocalDateTime now = LocalDateTime.now();
        // 生效时间
        claims.put(JWTPayload.NOT_BEFORE, now);
        // 过期时间：30 min
        claims.put(JWTPayload.EXPIRES_AT, now.plusMinutes(30));
        claims.put("uuid", loginResp.getBrowserUuid());
        // 生成 token
        String token = JWTUtil.createToken(claims, Constants.LOGIN_USER_KEY.getBytes());

        // 赋值
        resultModel.setUserId(userLoginResp.getId());
        resultModel.setRealName("张飞");
        resultModel.setUsername(userLoginResp.getUsername());
        resultModel.setToken(token);
        // 角色todo
        List<LoginResultModel.RoleInfo> roleInfoList = new ArrayList<>();
        LoginResultModel.RoleInfo roleInfo = new LoginResultModel.RoleInfo();
        roleInfo.setRoleName("Super Admin");
        roleInfo.setValue("super");
        roleInfoList.add(roleInfo);
        resultModel.setRoles(roleInfoList);
        return resultModel;
    }

    /**
     * 匿名登录：浏览器标识唯一判断
     * @param req
     * @return
     */
    public LoginResultModel anonymousLogin(RegisterReq req) {
        // 返回封装登陆对象
        LoginResultModel resultModel = new LoginResultModel();

        String browserUuid = req.getBrowserUuid();
        if(StrUtil.isBlank(browserUuid)){
            throw new BizException(ResultStatus.Common.NOT_FOUNT_BROWSER_UUID);
        }
        // 1. 获取数据库account_info by browser_uuid
        AccountInfo accountInfo = accountInfoMapper.selectOneByBrowserUuid(req.getBrowserUuid());
        // 2. 不存在,进行初始化数据
        if(accountInfo == null){
            accountInfo = new AccountInfo();
            accountInfo.setBrowserUuid(browserUuid);
            accountInfoMapper.insert(accountInfo);
        }
        UserLoginResp userLoginResp = new UserLoginResp();
        userLoginResp.setId(accountInfo.getId());
        userLoginResp.setUsername(browserUuid);

        Map<String, Object> claims = new HashMap<>();
        claims.put("uuid", accountInfo.getBrowserUuid());
        LocalDateTime now = LocalDateTime.now();
        // 生效时间
        claims.put(JWTPayload.NOT_BEFORE, now);
        // 过期时间：5 min
        claims.put(JWTPayload.EXPIRES_AT, now.plusMinutes(5));
        // 一次性 token
        claims.put(JWTPayload.JWT_ID, Instant.now().getEpochSecond());
        String token = JWTUtil.createToken(claims, Constants.ANONYMOUS_LOGIN_USER_KEY.getBytes());

        // 赋值
        resultModel.setUserId(userLoginResp.getId());
        resultModel.setRealName("张飞");
        resultModel.setUsername(userLoginResp.getUsername());
        resultModel.setToken(token);
        // 角色todo
        List<LoginResultModel.RoleInfo> roleInfoList = new ArrayList<>();
        LoginResultModel.RoleInfo roleInfo = new LoginResultModel.RoleInfo();
        roleInfo.setRoleName("Super Admin");
        roleInfo.setValue("super");
        roleInfoList.add(roleInfo);
        resultModel.setRoles(roleInfoList);
        return resultModel;
    }

    /**
     * redis缓存账户对象
     * @param userLoginResp
     * @return
     */
    private UserLoginResp getUserLoginResp(UserLoginResp userLoginResp) {
        Account account = new Account();
        account.setId(userLoginResp.getId());
        account.setAccount(userLoginResp.getUsername());
        String token = AccountContext.storeSession(account);
        userLoginResp.setToken(token);
        // header内容
        userLoginResp.setHeader(HttpHeaders.AUTHORIZATION + ":" + "Bearer " + token);
        return userLoginResp;
    }

    public UserInfoResultModel getUserInfo() {
        return new UserInfoResultModel();
    }

    /**
     * 注册账号，注册公司
     * @param params
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean registerCompany(RegisterParams params) {
        // 校验: 用户名唯一
        String account = params.getAccount();
        Integer a_counts = this.accountInfoMapper.queryCountsByAccount(account);
        if(a_counts>0){
            throw new BizException(ResultStatus.Auth.ERROR_ACCOUNT_EXIST);
        }
        // 校验：公司名称唯一
        String companyName = params.getCompanyName();
        Integer c_counts = this.companyInfoMapper.queryCountsByCompanyName(companyName);
        if(c_counts>0){
            throw new BizException(ResultStatus.Auth.ERROR_COMPANY_EXIST);
        }
        // 1. 保存账号信息
        AccountInfo accountInfo = new AccountInfo();
        accountInfo.setAccount(account);
        accountInfo.setPassword(params.getPassword());
        accountInfo.setBrowserUuid(params.getBrowserUuid());
        this.accountInfoMapper.insert(accountInfo);
        // 2. 保存公司信息
        String companyAccounts =
                DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + "001";
        CompanyInfo companyInfo = new CompanyInfo();
        companyInfo.setCompanyName(companyName);
        companyInfo.setCompanyAccounts(companyAccounts);
        this.companyInfoMapper.insert(companyInfo);
        // 3. 关联账号和公司信息
        Integer accountId = accountInfo.getId();
        Integer companyId = companyInfo.getId();
        // 该用户虽然可以关联多个公司，自己申请注册公司的时候绝对是木有滴
        AccountCompanyRelation hasRelation =
                this.relationMapper.queryRelationByIds(accountId, companyId);
        // null 时添加申请的关联关系
        if (Objects.isNull(hasRelation)) {
            // 申请注册公司的用户关联起来
            AccountCompanyRelation relation = new AccountCompanyRelation();
            relation.setCompanyId(companyId);
            relation.setAccountId(accountId);
            this.relationMapper.insert(relation);
        }
        return true;
    }

    /**
     * 修改密码
     * @param params
     * @return
     */
    public boolean changePwd(ChangePwdParams params) {
        // 1.原密码是否正确
        String passwordOld = params.getPasswordOld();
        String passwordNew = params.getPasswordNew();
//        this.accountInfoMapper.queryCountsByPassword(passwordOld);
        // 2.更新新密码
        return true;
    }
}
